#include <ESP8266WiFi.h>
#include <PubSubClient.h>
// #define MQTT_MAX_PACKET_SIZE 512
// #define MQTT_KEEPALIVE 300
#include <ArduinoJson.h>
#include "DHT.h"
#include "SHA256.h"
// #include <iostream>
#define DHT11PIN D7
#define DHTTYPE DHT11
DHT mydht(DHT11PIN, DHTTYPE);

#define WIFI_SSID "LAGRANGE"
#define WIFI_PASSWD "abcdef.."

/* 设备的三元组信息*/
#define PRODUCT_KEY "a1VTmr8pzbw"
#define DEVICE_NAME "device1"
#define DEVICE_SECRET "f31f2640e54c1055f7042de20ceb6fdb"
#define REGION_ID "cn-shanghai"
// {
//   "ProductKey": "a1VTmr8pzbw",
//   "DeviceName": "device1",
//   "DeviceSecret": "f31f2640e54c1055f7042de20ceb6fdb"
// }
/* 线上环境域名和端口号，不需要改 */
#define MQTT_SERVER PRODUCT_KEY ".iot-as-mqtt." REGION_ID ".aliyuncs.com"
#define MQTT_PORT 1883
#define MQTT_USRNAME DEVICE_NAME "&" PRODUCT_KEY

// #define CLIENT_ID "ESP82661|securemode=3,timestamp=%d,signmethod=hmacsha256|"
// 算法工具: http://iot-face.oss-cn-shanghai.aliyuncs.com/tools.htm 进行加密生成password
// password教程 https://www.yuque.com/cloud-dev/iot-tech/mebm5g
// #define MQTT_PASSWD       "参考上面password教程，算法工具生成，如果上面的教程看不懂就看文章下面"

#define CONTENT_STR_FORMAT "clientIdesp82661deviceName" DEVICE_NAME "productKey" PRODUCT_KEY "timestamp%d"
#define ALINK_BODY_FORMAT "{\"id\":\"esp82661\",\"version\":\"1.0\",\"method\":\"thing.event.property.post\",\"params\":%s}"
#define ALINK_TOPIC_PROP_POST "/sys/" PRODUCT_KEY "/" DEVICE_NAME "/thing/event/property/post"
#define COMMAND_TOPIC "/" PRODUCT_KEY "/" DEVICE_NAME "/user/command"

WiFiClient espClient;
PubSubClient client(espClient);

// char CLIENT_ID[80];
// String STR1;
// String str1;
String client_id;
String CLIENT_ID;
char MQTT_PASSWD[64];
int lasttime = 0;

static void hex_to_str(char *ptr, char *buf, int len);
void mqttCheckConnect();
void wifiInit();
void callback(char *topic, byte *payload, unsigned int length);
void command_topic_handle(char *payload);
void setup()
{
    Serial.begin(115200);
    Serial.println();
    pinMode(LED_BUILTIN, OUTPUT);
    mydht.begin();
    delay(100);
    char hashs[32];
    char buff[128];
    int timestamp = millis();
    timestamp = 123456;
    // Serial.print(timestamp);
    // Serial.println(timestamp);
    sprintf(buff, CONTENT_STR_FORMAT, timestamp);
    // sprintf(CLIENT_ID, "esp82661|securemode=3,signmethod=hmacsha256,timestamp=%d|", timestamp);
    // CLIENT_ID = "esp82661|securemode=3,signmethod=hmacsha256,timestamp=" + String(timestamp) + "|";
    // str1 = "esp82661|securemode=3,signmethod=hmacsha256,timestamp=" + String(timestamp) + "|";
    client_id = "esp82661|securemode=3,signmethod=hmacsha256,timestamp=" + String(timestamp) + "|";
    // STR1 = "esp82661|securemode=3,signmethod=hmacsha256,timestamp=" + String(timestamp) + "|";
    // Serial.println(CLIENT_ID);
    // Serial.println(str1);
    Serial.println(client_id);
    // Serial.println(STR1);
    SHA256 sha256;
    sha256.resetHMAC(DEVICE_SECRET, strlen(DEVICE_SECRET));
    sha256.update(buff, strlen(buff));
    sha256.finalizeHMAC(DEVICE_SECRET, strlen(DEVICE_SECRET), hashs, 32);
    hex_to_str(MQTT_PASSWD, hashs, 32);

    delay(100);
    wifiInit();

    client.setServer(MQTT_SERVER, MQTT_PORT); /* 连接WiFi之后，连接MQTT服务器 */
    client.setCallback(callback);
    client.subscribe("/a1VTmr8pzbw/device1/user/command");
    mqttCheckConnect();
    lasttime = millis();
}

void loop()
{
    float ht = mydht.readTemperature();
    float hh = mydht.readHumidity();
    char param[128];
    char jsonbuf[256];
    if (isnan(ht) || isnan(hh))
    {
        Serial.println("Read DH11 Fail");
        delay(100);
    }
    else
    {
        if (millis()-lasttime > 5000)
        {
            Serial.print("Humidity:"); //向串口打印 Humidity:
            Serial.print(hh);          //向串口打印湿度数据
            Serial.print("%");
            Serial.print("  Temperature:");
            Serial.print(ht); //向串口打印温度数据
            Serial.println("C");
            sprintf(param, "{\"temperature\":%.1f,\"Humidity\":%.1f}", ht, hh);
            sprintf(jsonbuf, ALINK_BODY_FORMAT, param);
            Serial.println(jsonbuf);
            boolean d = client.publish(ALINK_TOPIC_PROP_POST, jsonbuf);
            if (d)
            {
                Serial.println("publish T and H success");
                digitalWrite(LED_BUILTIN, LOW);
                delay(200);
                digitalWrite(LED_BUILTIN, HIGH);
            }
            else
            {
                Serial.println("publish T and H fail");
                mqttCheckConnect();
            }
            lasttime = millis();
        }
    }
    client.loop(); //检查云端是否有数据下发
}

void wifiInit()
{
    WiFi.mode(WIFI_STA);
    WiFi.begin(WIFI_SSID, WIFI_PASSWD); //连接WiFi
    while (WiFi.status() != WL_CONNECTED)
    {
        delay(1000);
        Serial.println("WiFi not Connect");
    }
    Serial.println("Connected to AP");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());
    Serial.print("espClient ");
}

void mqttCheckConnect()
{
    while (!client.connected())
    {
        Serial.println("Connecting to MQTT Server ...");
        // Serial.println(str1.c_str());
        // Serial.println(STR1.c_str());
        // Serial.println(CLIENT_ID.c_str());
        // Serial.println(CLIENT_ID);
        // Serial.println(String(CLIENT_ID));
        // Serial.println(sizeof(CLIENT_ID));
        // Serial.println(CLIENT_ID.length());
        // if (client.connect("esp82661|securemode=3,signmethod=hmacsha256,timestamp=123456|", MQTT_USRNAME, MQTT_PASSWD))
        // if (client.connect(CLIENT_ID.c_str(), MQTT_USRNAME, MQTT_PASSWD))
        // if (client.connect(str1.c_str(), MQTT_USRNAME, MQTT_PASSWD))
        if (client.connect(client_id.c_str(), MQTT_USRNAME, MQTT_PASSWD))
        // if (client.connect(STR1.c_str(), MQTT_USRNAME, MQTT_PASSWD))
        {
            Serial.println("MQTT Connected!");
        }
        else
        {
            Serial.println("ERR INFO:");
            Serial.println(client_id);
            Serial.println(MQTT_USRNAME);
            Serial.println(MQTT_PASSWD);
            Serial.print("MQTT Connect err:");
            Serial.println(client.state());
            delay(5000);
        }
    }
}

static void hex_to_str(char *ptr, char *buf, int len)
{
    for (int i = 0; i < len; i++)
    {
        sprintf(ptr, "%02x", buf[i]);
        ptr += 2;
    }
}

void callback(char *topic, byte *payload, unsigned int length)
{
    Serial.print("Message arrived [");
    Serial.print(topic);
    Serial.println("] ");
    payload[length] = '\0';
    Serial.println((char *)payload);
    // if (topic == COMMAND_TOPIC)
    // {
    command_topic_handle((char *)payload);
    // }
}

void command_topic_handle(char *payload)
{
    // Serial.println("command_topic_handle :");
    // StaticJsonBuffer<100> jsonBuffer;
    // // StaticJsonBuffer 在栈区分配内存   它也可以被 DynamicJsonBuffer（内存在堆区分配） 代替
    // // DynamicJsonBuffer  jsonBuffer;
    // JsonObject &root = jsonBuffer.parseObject(payload);

    DynamicJsonDocument doc(1024);
    deserializeJson(doc, payload);

    int command = doc["params"]["command"];
    if (command == 1)
    {
        digitalWrite(LED_BUILTIN, LOW);
    }
    else if (command == 0)
    {
        digitalWrite(LED_BUILTIN, HIGH);
    }

    // Test if parsing succeeds.
    // if (!root.success())
    // {
    //     Serial.println("parseObject() failed");
    //     return;
    // }

    // String deviceName = root["dn"];
    // int status = root["s"];

    // if (strcmp(DEVICE_NAME, deviceName.c_str()) == 0)
    // {
    // if (status == 1)
    // {
    //     digitalWrite(LED_BUILTIN, LOW);
    // }
    // else
    // {
    //     digitalWrite(LED_BUILTIN, HIGH);
    // }
    // }
}